package com.zp.ojcodesandbox;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.dfa.FoundWord;
import cn.hutool.dfa.WordTree;
import com.zp.ojcodesandbox.model.ExecuteCodeRequest;
import com.zp.ojcodesandbox.model.ExecuteCodeSandResponse;
import com.zp.ojcodesandbox.model.ExecuteMessage;
import com.zp.ojcodesandbox.model.JudgeInfo;
import com.zp.ojcodesandbox.util.ProcessRunAndMessage;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

public class JavaNativeCodeSandBoxOld implements CodeSandBox{

    private static final String Global_Code_Dir_Name = "tmpCode";
    private static final String Define_Java_class_name = "Main.java";

    private static final Long Java_Run_Time = 1000L;
    // 黑名单
    private static final List<String> blackList =Arrays.asList("File", "Process", "exec");
    private static final WordTree WORD_TREE = new WordTree();

    static {
        WORD_TREE.addWords(blackList);
    }

    public static void main(String[] args){
       JavaNativeCodeSandBox javaNativeCodeSandBox = new JavaNativeCodeSandBox();
       ExecuteCodeRequest executeCodeRequest = new ExecuteCodeRequest();
        executeCodeRequest.setInputs(Arrays.asList("1 2", "3 4"));
        String code = ResourceUtil.readStr("testCode/Main.java", StandardCharsets.UTF_8);
        executeCodeRequest.setCode(code);
        executeCodeRequest.setLanguage("java");
        ExecuteCodeSandResponse executeCodeSandResponse = javaNativeCodeSandBox.executeCode(executeCodeRequest);
        System.out.println(executeCodeSandResponse);
    }
    @Override
    public ExecuteCodeSandResponse executeCode(ExecuteCodeRequest executeCodeRequest) {

        List<String> inputs = executeCodeRequest.getInputs();
        String code = executeCodeRequest.getCode();
        String language = executeCodeRequest.getLanguage();
        // 搜索具有指定名称的公共属性
        String userDir = System.getProperty("user.dir");
        // 使用File.separator文件分隔符提高适用性
        String globalCodePathName = userDir + File.separator + Global_Code_Dir_Name;
        // 不存在文件就创建
        if(!FileUtil.exist(globalCodePathName)){
            FileUtil.mkdir(globalCodePathName);
        }
        // 将用户代码保存成文件
        String userCodeDir = globalCodePathName + File.separator + UUID.randomUUID();
        String userCodePath = userCodeDir + File.separator + Define_Java_class_name;
        File userCodeFile = FileUtil.writeString(code, userCodePath, StandardCharsets.UTF_8);

        ExecuteCodeSandResponse executeCodeSandResponse = new ExecuteCodeSandResponse();

        // 检查敏感词
        FoundWord foundWord = WORD_TREE.matchWord(code);
        if(foundWord != null){
            System.out.println("include Sensitive words" + foundWord);
            executeCodeSandResponse.setMessage("包含敏感词" + foundWord);
            return executeCodeSandResponse;
        }

        // 编译代码
        String complied = String.format("javac -encoding utf-8 " + userCodeFile.getAbsolutePath());
        try {
            // 运行控制台语句
            Process compiledProcess = Runtime.getRuntime().exec(complied);
            ExecuteMessage executeMessage = ProcessRunAndMessage.execute(compiledProcess, "complied");
            System.out.println(executeMessage);
        }catch (Exception e){
            return getErrorResponse(e, executeCodeRequest);
        }

        // 执行代码
        List<ExecuteMessage> errorManagerList = new ArrayList<>();
        for (String inputArgs: executeCodeRequest.getInputs()){
            String runCmd = String.format("java -Dfile.encoding=UTF-8 -cp %s Main %s", userCodeDir, inputArgs );
            try {
                Process runProcess = Runtime.getRuntime().exec(runCmd);
                // 超时控制
                new Thread(() -> {
                    try {
                        Thread.sleep(Java_Run_Time);
                        Integer exitValue = runProcess.exitValue();
                        if(exitValue == null){
                            System.out.println("over time");
                            runProcess.destroy();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }).start();

                ExecuteMessage runMessage = ProcessRunAndMessage.execute(runProcess, "运行");
                errorManagerList.add(runMessage);
            } catch (IOException e) {
                return getErrorResponse(e, executeCodeRequest);
            }
        }
        // 拼接返回信息
        // 最长时间
        Long maxTime = 0L;
        List<String> outputsList = new ArrayList<>();
        executeCodeSandResponse.setStatus(1);
        for (ExecuteMessage executeMessage: errorManagerList){
            if(executeMessage.getErrorMessage() != null){
                executeCodeSandResponse.setMessage(executeMessage.getErrorMessage());
                executeCodeSandResponse.setStatus(3);
                break;
            }
            if(executeMessage.getTime() != null){
                maxTime = Math.max(maxTime, executeMessage.getTime());
            }
            outputsList.add(executeMessage.getErrorMessage());
        }
        executeCodeSandResponse.setOutputs(outputsList);
        executeCodeSandResponse.setLanguage(executeCodeRequest.getLanguage());
        JudgeInfo judgeInfo = new JudgeInfo();
        judgeInfo.setTime(maxTime);
        executeCodeSandResponse.setJudgeInfo(judgeInfo);

        // 文件清理
        if(userCodeFile.getParentFile() != null){
            boolean delete = FileUtil.del(userCodeDir);
            System.out.println(String.format("删除%s", delete ? "success":"fail"));
        }
        return executeCodeSandResponse;
    }

    /**
     * 错误处理方法 返回上述代码无法处理的错误信息
     * @param e
     * @param executeCodeRequest
     * @return
     */
    private ExecuteCodeSandResponse getErrorResponse(Throwable e, ExecuteCodeRequest executeCodeRequest){
        ExecuteCodeSandResponse executeCodeSandResponse = new ExecuteCodeSandResponse();
        executeCodeSandResponse.setOutputs(new ArrayList<>());
        executeCodeSandResponse.setMessage(e.getMessage());
        executeCodeSandResponse.setStatus(2);
        executeCodeSandResponse.setLanguage(executeCodeRequest.getLanguage());
        executeCodeSandResponse.setJudgeInfo(new JudgeInfo());
        return executeCodeSandResponse;
    }
}
